package cn.com.bluemoon.common;


import cn.com.bluemoon.daps.common.domain.ResultBean;
import cn.com.bluemoon.daps.common.domain.UserPermissionApi;
import cn.com.bluemoon.daps.common.exception.DapThrowException;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;


/**
 * 定义接口请求获取用户创建人id的注解
 * 约束目前支持的三种类型
 *
 * @author Jarod.Kong
 * @date 2020/11/3 12:03
 */
@Slf4j
public enum KeyAnnotationClz {
    /**
     * pathvariable
     */
    PathVariable(org.springframework.web.bind.annotation.PathVariable.class) {
        @Override
        public String[] getModelId(ProceedingJoinPoint pjpAop, HttpServletRequest request, String modelKey) throws DapThrowException {
            BmAssetUtils.notNull(modelKey, "模型id对应的属性名不可为空");
            // aop 处理
            // httpRequest
            if (request == null) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                if (attributes != null) {
                    request = attributes.getRequest();
                }
            }
            return getParamValuesByHttpRequestAndParamKey(modelKey, request);
        }

        public String[] getParamValuesByHttpRequestAndParamKey(String paramKey, HttpServletRequest request) {
            BmAssetUtils.notNull(request, "获取web HttpRequest失败！！");
            log.info("针对挂载了@UserPermission.SingleRow的接口{}方法进行用户操作权限验证", request.getRequestURI());
            @SuppressWarnings("unchecked")
            Map<String, Object> pathVariables = (Map<String, Object>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            String modelId = String.valueOf(pathVariables.getOrDefault(paramKey, ""));
            return new String[]{modelId};
        }

        @Override
        public String getModelId(ProceedingJoinPoint pjp) {
            Method requestMethod = ((MethodSignature) pjp.getSignature()).getMethod();
            UserPermission.SingleRow userPerm = requestMethod.getAnnotation(UserPermission.SingleRow.class);
            if (userPerm != null) {
                String modelIdFieldName = userPerm.key();
                String[] modelIds = getModelId(pjp, null, modelIdFieldName);
                if (modelIds.length > 0) return modelIds[0];
            }
            return null;
        }
    },
    /**
     * RequestParam
     */
    RequestParam(org.springframework.web.bind.annotation.RequestParam.class) {
        @Override
        public String[] getModelId(ProceedingJoinPoint pjpAop, HttpServletRequest request, String modelKey) throws DapThrowException {
            BmAssetUtils.notNull(modelKey, "模型id对应的属性名不可为空");
            // aop 处理
            // httpRequest
            if (request == null) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                if (attributes != null) {
                    request = attributes.getRequest();
                }
            }
            BmAssetUtils.notNull(request, "获取web HttpRequest失败！！");
            log.info("针对挂载了@UserPermission.SingleRow的接口{}方法进行用户操作权限验证", request.getRequestURI());
            Map<String, String[]> parameterMap = request.getParameterMap();
            String[] modelIds = parameterMap.getOrDefault(modelKey, new String[0]);
            return modelIds;
        }

        @Override
        public String getModelId(ProceedingJoinPoint pjp) {
            Method requestMethod = ((MethodSignature) pjp.getSignature()).getMethod();
            UserPermission.SingleRow userPerm = requestMethod.getAnnotation(UserPermission.SingleRow.class);
            if (userPerm != null) {
                String modelIdFieldName = userPerm.key();
                String[] modelIds = getModelId(pjp, null, modelIdFieldName);
                if (modelIds.length > 0) return modelIds[0];
            }
            return null;
        }
    },
    /**
     * RequestBody
     */
    RequestBody(org.springframework.web.bind.annotation.RequestBody.class) {
        @SuppressWarnings("unchecked")
        @Override
        public String[] getModelId(ProceedingJoinPoint pjpAop, HttpServletRequest request, String modelKey) throws DapThrowException {
            if (pjpAop != null) {
                String modelId = null;
                Object[] requestParams = pjpAop.getArgs();
                Method requestMethod = ((MethodSignature) pjpAop.getSignature()).getMethod();
                Parameter[] parameters = requestMethod.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    if (parameter.isAnnotationPresent(RequestBody.class)) {
                        Object requestParam = requestParams[i];
                        if (requestParam instanceof Collection) {
                            @SuppressWarnings("unchecked") Collection<Object> modelIds = (Collection<Object>) requestParam;
                            if (!modelIds.isEmpty()) {
                                BmAssetUtils.isTrue(modelIds.size() == 1, "权限控制：只允许同时操作一条数据");
                                Object mId = modelIds.stream().findFirst().orElse(null);
                                modelId = Objects.toString(mId, null);
                            }
                        } else if (requestParam instanceof Object[]) {
                            Object[] modelIds = (Object[]) requestParam;
                            if (modelIds.length > 0) {
                                BmAssetUtils.isTrue(modelIds.length == 1, "权限控制：只允许同时操作一条数据");
                                modelId = Objects.toString(modelIds[0], null);
                            }
                        } else if (requestParam instanceof Map) {
                            @SuppressWarnings({"unchecked"})
                            Map<Object, Object> map = (Map<Object, Object>) requestParam;
                            modelId = Objects.toString(map.getOrDefault(modelKey, null), null);
                        } else if (requestParam instanceof UserPermissionApi) {
                            @SuppressWarnings({"rawtypes"})
                            UserPermissionApi api = (UserPermissionApi) requestParam;
                            modelId = Objects.toString(api.getUserPermissionKeyValue(), null);
                        } else {
                            log.error("若要使用@RequestBody来制定用户操作权限校验，则加上接口{}或Collection或Map,Object[],提供获取实体唯一主键的接口", UserPermissionApi.class.getCanonicalName());
                            throw new DapThrowException("权限控制：获取操作对象唯一标识失败，不可操作", ResultBean.UNAUTHORIZED.getCode());
                        }
                    }
                }
                if (modelId != null) {
                    return new String[]{modelId};
                }
            }
            throw new DapThrowException("当前模型数据权限控制中基于HttpRequest的支持RequestBody方式获取模型id");
        }

        @Override
        public String getModelId(ProceedingJoinPoint pjp) {
            Method requestMethod = ((MethodSignature) pjp.getSignature()).getMethod();
            UserPermission.SingleRow userPerm = requestMethod.getAnnotation(UserPermission.SingleRow.class);
            if (userPerm != null) {
                String modelIdFieldName = userPerm.key();
                String[] modelIds = getModelId(pjp, null, modelIdFieldName);
                if (modelIds.length > 0) return modelIds[0];
            }
            return null;
        }
    };

    private final Class<? extends Annotation> anno;

    KeyAnnotationClz(Class<? extends Annotation> anno) {
        this.anno = anno;
    }

    public abstract String[] getModelId(ProceedingJoinPoint pjpAop, HttpServletRequest request, String modelKey) throws DapThrowException;

    /**
     * 根据含有注解{@link UserPermission.SingleRow}的切面入参，进行分析返回modelId
     *
     * @param pjp 切面入参
     * @return 返回modelId
     */
    public String getModelId(ProceedingJoinPoint pjp) {
        throw new UnsupportedOperationException("当前类型不支持获取模型id");
    }

    /**
     * 根据含有注解{@link HttpServletRequest}的入参，进行分析返回modelId[]
     *
     * @param request  httpRequest
     * @param paramKey 参数key
     * @return 参数[]
     */
    public String[] getParamValuesByHttpRequest(HttpServletRequest request, String paramKey) {
        throw new UnsupportedOperationException("当前类型不支持获取模型id");
    }

    public Class<? extends Annotation> getAnno() {
        return anno;
    }

}